package dao

import javax.inject.{Inject, Singleton}
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import slick.jdbc.JdbcProfile

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import models.Tables._
import tool.Pojo.{AdvancedFilterData, BrowseData, PageData, QueryPageData}

/**
 * Created by yz on 2018/7/17
 */
@Singleton
class JunctionDao @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends
  HasDatabaseConfigProvider[JdbcProfile] {

  import utils.MyPostgresProfile.api._

  val table = Junction
  type MyRow = JunctionRow

  def insertAll(rows: List[JunctionRow]) = db.run(Junction ++= rows).map(_ => ())

  def deleteAll(kind: String) = db.run(Junction.filter(_.kind === kind).delete).map(_ => ())

  def selectAllGeneTypes = db.run(Junction.map(_.geneType).distinct.result)

  def selectAllGeneSymbols = db.run(Junction.map(_.geneSymbol).distinct.result)

  def selectAll(data: AdvancedFilterData, junctionIds: Seq[String]) = db.run(Junction.
    filter { x =>
      x.junctionId.inSetBind(junctionIds)
    }.filter { x =>
    data.junctionTypes.map(y => x.junctionType.inSetBind(y)).getOrElse(LiteralColumn(true))
  }.filter { x =>
    data.annotations.map(y => x.annotation.inSetBind(y)).getOrElse(LiteralColumn(true))
  }.filter { x =>
    data.tumorTypes.map { ys =>
      val b = ys.map { y =>
        x.tumorSpecificTypes.like(s"%${y}%")
      }.reduceLeft((t, z) => t || z)
      b
    }.getOrElse(LiteralColumn(true))
  }.result)

  def selectAllLocation = db.run(Junction.map(_.junctionLocation).distinct.result)

  def selectAll(geneSymbols: List[String]) = db.run(Junction.filter(_.geneSymbol.
    inSetBind(geneSymbols)).result)

  def selectAllByJunctionIds(junctionIds: List[String]) = db.run(Junction.
    filter(_.junctionId.
      inSetBind(junctionIds)).result)

  def selectAll(geneSymbol: String) = db.run(Junction.
    filter(_.geneSymbol === geneSymbol).result)

  def selectAll = db.run(Junction.map(x => (x.junctionId, x.junctionLocation)).result)

  def select(junctionId: String) = db.run(Junction.
    filter(_.junctionId === junctionId).result.head)

  def selectAllJunctionTypes = db.run(Junction.map(_.junctionType).distinct.result)

  def selectAllAnnotation = db.run(Junction.map(_.annotation).distinct.result)

  def selectAllTumorTypes = db.run(Junction.map(_.tumorSpecificTypes).distinct.result)

  def selectAll(data: BrowseData) = db.run(Junction.filter { x =>
    data.geneTypes.map(y => x.geneType.inSetBind(y)).getOrElse(LiteralColumn(true))
  }.result)

  def deleteUnknownGeneType = db.run(Junction.filter(_.geneType === "#NAME?").delete).map(_ => ())

  def selectAll(data: PageData) = {
    val filterSamples = Junction.filter { x =>
      data.geneTypes.map(y => x.geneType.inSetBind(y)).getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.junctionTypes.map(y => x.junctionType.inSetBind(y)).getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.annotations.map(y => x.annotation.inSetBind(y)).getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.tumorTypes.map { ys =>
        val allB = ys.map { y =>
          x.tumorSpecificTypes.like(s"%${y}%")
        }
        val b = data.tumorTypesMethod match {
          case "union" => allB.reduceLeft((t, z) => t || z)
          case "inter" => allB.reduceLeft((t, z) => t && z)
        }
        b
      }.getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.geneSymbols.map(y => x.geneSymbol.inSetBind(y)).getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.search.map { y =>
        val likeY = s"%${y}%"
        x.junctionId.like(likeY) || x.junctionLocation.like(likeY) || x.geneSymbol.like(likeY) ||
          x.transcriptId.like(likeY) || x.tumorMedian.like(likeY) || x.ntMedian.like(likeY) ||
          x.normalMedian.like(likeY) || x.alternativeSplicingTypes.like(likeY)
      }.getOrElse(LiteralColumn(true))
    }
    val sortSamples = data.sort match {
      case None => filterSamples
      case Some(x) =>
        val order = data.order
        x match {
          case "tumorMedian" =>
            order match {
              case "asc" => filterSamples.sortBy(_.tumorMedian)
              case "desc" => filterSamples.sortBy(_.tumorMedian.desc)
            }
          case "ntMedian" =>
            order match {
              case "asc" => filterSamples.sortBy(_.ntMedian)
              case "desc" => filterSamples.sortBy(_.ntMedian.desc)
            }
          case "normalMedian" =>
            order match {
              case "asc" => filterSamples.sortBy(_.normalMedian)
              case "desc" => filterSamples.sortBy(_.normalMedian.desc)
            }
          case "tumorFreq" =>
            order match {
              case "asc" => filterSamples.sortBy(_.tumorFreq)
              case "desc" => filterSamples.sortBy(_.tumorFreq.desc)
            }
          case "ntFreq" =>
            order match {
              case "asc" => filterSamples.sortBy(_.ntFreq)
              case "desc" => filterSamples.sortBy(_.ntFreq.desc)
            }
          case "normalFreq" =>
            order match {
              case "asc" => filterSamples.sortBy(_.normalFreq)
              case "desc" => filterSamples.sortBy(_.normalFreq.desc)
            }
          case _ =>
            filterSamples
        }
    }

    val finalSamples = sortSamples
    val countF = finalSamples.length.result
    val resultF = finalSamples.drop(data.offset).take(data.limit).result
    val f = countF.zip(resultF)
    db.run(f)
  }

  def selectAll(data: QueryPageData) = {
    val filterSamples = Junction.filter { x =>
      data.geneTypes.map(y => x.geneType.inSetBind(y)).getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.junctionTypes.map(y => x.junctionType.inSetBind(y)).getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.annotations.map(y => x.annotation.inSetBind(y)).getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.tumorTypes.map { ys =>
        val b = ys.map { y =>
          x.tumorSpecificTypes.like(s"%${y}%")
        }.reduceLeft((t, z) => t || z)
        b
      }.getOrElse(LiteralColumn(true))
    }.filter { x =>
      data.search.map { y =>
        val likeY = s"%${y}%"
        x.junctionId.like(likeY) || x.junctionLocation.like(likeY) || x.geneSymbol.like(likeY) ||
          x.transcriptId.like(likeY) || x.tumorMedian.like(likeY) || x.ntMedian.like(likeY) ||
          x.normalMedian.like(likeY) || x.alternativeSplicingTypes.like(likeY)
      }.getOrElse(LiteralColumn(true))
    }
    val resultF = filterSamples.result
    val f = resultF
    db.run(f)
  }

  def insertOrUpdates(rows: Seq[MyRow]) = {
    val action = {
      val kind = rows.head.kind
      val delete = table.filter(_.kind === kind).delete
      val insertAll = table ++= rows
      delete.flatMap(_ => insertAll)
    }.transactionally
    db.run(action).map(_ => ())
  }


}
